// +build ignore

package main

import (
	"bytes"
	"fmt"
	"go/format"
	"log"
	"os"
)

var goBuf bytes.Buffer
var goTestBuf bytes.Buffer

type cdef struct {
	cName  string
	goName string
	cType  string
	goType string
	val    interface{}
}

var cdefs = []cdef{
	// AVERROR_*
	{cName: "AVERROR_BSF_NOT_FOUND", goName: "ErrorCodeBSFNotFound", cType: "int", goType: "ErrorCode", val: -1179861752},
	{cName: "AVERROR_BUG", goName: "ErrorCodeBug", cType: "int", goType: "ErrorCode", val: -558323010},
	{cName: "AVERROR_BUFFER_TOO_SMALL", goName: "ErrorCodeBufferTooSmall", cType: "int", goType: "ErrorCode", val: -1397118274},
	{cName: "AVERROR_DECODER_NOT_FOUND", goName: "ErrorCodeDecoderNotFound", cType: "int", goType: "ErrorCode", val: -1128613112},
	{cName: "AVERROR_DEMUXER_NOT_FOUND", goName: "ErrorCodeDemuxerNotFound", cType: "int", goType: "ErrorCode", val: -1296385272},
	{cName: "AVERROR_ENCODER_NOT_FOUND", goName: "ErrorCodeEncoderNotFound", cType: "int", goType: "ErrorCode", val: -1129203192},
	{cName: "AVERROR_EOF", goName: "ErrorCodeEOF", cType: "int", goType: "ErrorCode", val: -541478725},
	{cName: "AVERROR_EXIT", goName: "ErrorCodeExit", cType: "int", goType: "ErrorCode", val: -1414092869},
	{cName: "AVERROR_EXTERNAL", goName: "ErrorCodeExternal", cType: "int", goType: "ErrorCode", val: -542398533},
	{cName: "AVERROR_FILTER_NOT_FOUND", goName: "ErrorCodeFilterNotFound", cType: "int", goType: "ErrorCode", val: -1279870712},
	{cName: "AVERROR_INVALIDDATA", goName: "ErrorCodeInvalidData", cType: "int", goType: "ErrorCode", val: -1094995529},
	{cName: "AVERROR_MUXER_NOT_FOUND", goName: "ErrorCodeMuxerNotFound", cType: "int", goType: "ErrorCode", val: -1481985528},
	{cName: "AVERROR_OPTION_NOT_FOUND", goName: "ErrorCodeOptionNotFound", cType: "int", goType: "ErrorCode", val: -1414549496},
	{cName: "AVERROR_PATCHWELCOME", goName: "ErrorCodePatchWelcome", cType: "int", goType: "ErrorCode", val: -1163346256},
	{cName: "AVERROR_PROTOCOL_NOT_FOUND", goName: "ErrorCodeProtocolNotFound", cType: "int", goType: "ErrorCode", val: -1330794744},
	{cName: "AVERROR_STREAM_NOT_FOUND", goName: "ErrorCodeStreamNotFound", cType: "int", goType: "ErrorCode", val: -1381258232},
	{cName: "AVERROR_BUG2", goName: "ErrorCodeBug2", cType: "int", goType: "ErrorCode", val: -541545794},
	{cName: "AVERROR_UNKNOWN", goName: "ErrorCodeUnknown", cType: "int", goType: "ErrorCode", val: -1313558101},
	{cName: "AVERROR_EXPERIMENTAL", goName: "ErrorCodeExperimental", cType: "int", goType: "ErrorCode", val: -733130664},
	{cName: "AVERROR_INPUT_CHANGED", goName: "ErrorCodeInputChanged", cType: "int", goType: "ErrorCode", val: -1668179713},
	{cName: "AVERROR_OUTPUT_CHANGED", goName: "ErrorCodeOutputChanged", cType: "int", goType: "ErrorCode", val: -1668179714},
	{cName: "AVERROR_HTTP_BAD_REQUEST", goName: "ErrorCodeHttpBadRequest", cType: "int", goType: "ErrorCode", val: -808465656},
	{cName: "AVERROR_HTTP_UNAUTHORIZED", goName: "ErrorCodeHttpUnauthorized", cType: "int", goType: "ErrorCode", val: -825242872},
	{cName: "AVERROR_HTTP_FORBIDDEN", goName: "ErrorCodeHttpForbidden", cType: "int", goType: "ErrorCode", val: -858797304},
	{cName: "AVERROR_HTTP_NOT_FOUND", goName: "ErrorCodeHttpNotFound", cType: "int", goType: "ErrorCode", val: -875574520},
	{cName: "AVERROR_HTTP_OTHER_4XX", goName: "ErrorCodeHttpOther4xx", cType: "int", goType: "ErrorCode", val: -1482175736},
	{cName: "AVERROR_HTTP_SERVER_ERROR", goName: "ErrorCodeHttpServerError", cType: "int", goType: "ErrorCode", val: -1482175992},
	// AV_*
	{cName: "AV_NOPTS_VALUE", goName: "NoPTSValue", cType: "int64_t", goType: "int64", val: -9223372036854775808},
}

func main() {
	writeGo()
	writeGoTest()
}

func die(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func printGo(a ...interface{}) {
	_, err := fmt.Fprintf(&goBuf, "%s", a...)
	die(err)
}

func writeGo() {
	str := `package avutil
//
// THIS FILE WAS AUTOMATICALLY GENERATED by hackgenerator.go.
//
`
	printGo(str)

	str = `
//
//#include <libavutil/avutil.h>
//
`
	printGo(str)

	for _, d := range cdefs {
		printGo("//\n")
		printGo(fmt.Sprintf("//%s GO_%s()\n", d.cType, d.cName))
		printGo("//{\n")
		printGo(fmt.Sprintf("//  return (%s)(%s);\n", d.cType, d.cName))
		printGo("//}\n")
	}

	str = `//
// #cgo pkg-config: libavutil
import "C"
`
	printGo(str)

	printGo("\n")
	printGo("const (\n")
	for _, d := range cdefs {
		printGo(fmt.Sprintf("%s %s = %d\n", d.goName, d.goType, d.val))
	}
	printGo(")\n")

	printGo("\n")
	for _, d := range cdefs {
		printGo("\n")
		printGo(fmt.Sprintf("func get%s() C.%s {\n", d.goName, d.cType))
		printGo(fmt.Sprintf("  return C.GO_%s()\n", d.cName))
		printGo("}\n")
	}

	f, err := os.Create("genhack.go")
	die(err)
	src, err := format.Source(goBuf.Bytes())
	die(err)
	_, err = f.Write(src)
	die(err)
	die(f.Close())
}

func printGoTest(a ...interface{}) {
	_, err := fmt.Fprintf(&goTestBuf, "%s", a...)
	die(err)
}

func writeGoTest() {
	str := `package avutil
//
// THIS FILE WAS AUTOMATICALLY GENERATED by hackgenerator.go.
//

import "testing"
`
	printGoTest(str)

	printGoTest("\n")
	for _, d := range cdefs {
		printGoTest("\n")
		printGoTest(fmt.Sprintf("func TestHackOverflow%s(t *testing.T) {\n", d.goName))
		printGoTest(fmt.Sprintf("cval := %s(get%s())\n", d.goType, d.goName))
		printGoTest(fmt.Sprintf("if cval != %s {\n", d.goName))
		printGoTest(fmt.Sprintf("t.Fatalf(\"%s/%s overflow check failed: expected %%v but got %%v\", %s, cval)", d.goName, d.cName, d.goName))
		printGoTest("}\n")
		printGoTest("}\n")
	}

	f, err := os.Create("genhack_test.go")
	die(err)
	src, err := format.Source(goTestBuf.Bytes())
	die(err)
	_, err = f.Write(src)
	die(err)
	die(f.Close())
}
